﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using AForge.Controls;
using AForge.Video.DirectShow;
using ArcFaceDemo.Utils;
using Emgu.CV;
using Emgu.CV.Face;
using Emgu.CV.Structure;
using Emgu.CV.Util;

namespace ArcFaceDemo
{
    public partial class FrmEmgu : Form
    {
        FilterInfoCollection videoDevices;
        VideoCaptureDevice videoSource;
        BackgroundWorker bgw = new BackgroundWorker();
        private VideoSourcePlayer videoSourcePlayer1;
        FaceRecognizer face_recognize = new LBPHFaceRecognizer();
        CascadeClassifier frontFace_detect = new CascadeClassifier(@".\haarcascades\haarcascade_frontalface_default.xml");
        CascadeClassifier profileFace_detect = new CascadeClassifier(@".\haarcascades\haarcascade_profileface.xml");
        string[] name;
        public FrmEmgu()
        {
            InitializeComponent();
            bgw.DoWork += Bgw_DoWork;
            bgw.WorkerSupportsCancellation = true;
            bgw.WorkerReportsProgress = true;
            bgw.ProgressChanged += Bgw_ProgressChanged;

            Train();
        }

        //提取特征值
        private void Train()
        {
            try
            {
                //将iamge_data中获得所有文件存在path中，后面对其进行切片处理，并且将取得的人脸名字存在name中
                string[] path = Directory.GetFiles("Image_data");
                Mat[] data = new Mat[path.Length];
                int[] label = new int[path.Length];
                name = new string[path.Length];
                for (int i = 0; i < path.Length; i++)
                {
                    Image<Gray, byte> image = new Image<Gray, byte>(path[i]);
                    data[i] = image.Mat;
                    label[i] = i;
                    //返回最后出现\\的索引
                    int a = path[i].LastIndexOf('\\');
                    //返回最后出现_的索引
                    int b = path[i].LastIndexOf('.');
                    //从a+1处开始截取字符，截取b-a-1个，这就是人脸名字在的区域
                    name[i] = path[i].Substring(a + 1, b - a - 1);
                }
                VectorOfInt image_lable = new VectorOfInt();
                VectorOfMat image_data = new VectorOfMat();
                image_data.Push(data);
                image_lable.Push(label);
                //训练图片
                face_recognize.Train(image_data, image_lable);
                face_recognize.Write("face/data.txt");
                //face_recognize.Read("face/data.txt");
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }
        private void AppendText(string message)
        {
            try
            {
                logBox.AppendText(message);
            }
            catch (Exception)
            {
            }
        }


        Mat scrRegister;
        private void btnSelectImageToRegister_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog dia = new OpenFileDialog();
                if (dia.ShowDialog() == DialogResult.OK)
                {
                    using (Mat scr = new Image<Gray, byte>(dia.FileName).Mat)
                    {
                        Rectangle[] recs = frontFace_detect.DetectMultiScale(scr);
                        if (recs.Length != 1)
                        {
                            AppendText("wrong picture, face count:" + recs.Length + " \r\r\n");
                            return;
                        }
                        if (scrRegister != null)
                        {
                            scrRegister.Dispose();
                        }
                        scrRegister = new Mat(scr, recs[0]);
                        pictureBoxSelected.Image = scrRegister.Bitmap;
                    }
                }
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btnRegisterFace_Click(object sender, EventArgs e)
        {
            try
            {
                string name = tbName.Text.Trim();
                if (string.IsNullOrEmpty(name))
                {
                    AppendText("请输入姓名\r\r\n");
                    return;
                }
                scrRegister.Save("Image_data" + "/" + name + ".jpg");
                Train();
            }
            catch (Exception err)
            {
                MessageBox.Show(err.Message);
            }
        }

        private void btnSelectImageToRecognize_Click(object sender, EventArgs e)
        {
            OpenFileDialog dia = new OpenFileDialog();
            if (dia.ShowDialog() == DialogResult.OK)
            {
                using (Mat scr = new Image<Gray, byte>(dia.FileName).Mat)
                {
                    Rectangle[] recs = frontFace_detect.DetectMultiScale(scr);
                    if (recs.Length != 1)
                    {
                        AppendText("wrong picture, face count:" + recs.Length + "\r\r\n");
                        return;
                    }
                    using (Mat scr2 = new Mat(scr, recs[0]))
                    {
                        FaceRecognizer.PredictionResult result = face_recognize.Predict(scr2);
                        //name中存的是上面文本框存的各个人脸对应的名字，与label也是一一对应的
                        AppendText(name[result.Label] + "   distance:" + result.Distance.ToString("0.00") + "\r\r\n");
                    }
                }
            }
        }

        private void btnVideo_Click(object sender, EventArgs e)
        {
            pnlVideo.Controls.Clear();
            videoSourcePlayer1 = new VideoSourcePlayer();
            pnlVideo.Controls.Add(videoSourcePlayer1);
            videoSourcePlayer1.Dock = DockStyle.Fill;
            videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
            videoSourcePlayer1.VideoSource = videoSource;
            videoSourcePlayer1.Start();
            bgw.RunWorkerAsync();
        }

        private void btnStopVideo_Click(object sender, EventArgs e)
        {
            try
            {
                if (bgw.IsBusy)
                {
                    bgw.CancelAsync();
                }
                pnlVideo.Controls.Clear();
                videoSourcePlayer1.Stop();
                videoSourcePlayer1.Dispose();
            }
            catch (Exception)
            {
                MessageBox.Show("关闭摄像头失败");
            }
        }

        private void Bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            while (true)
            {
                GC.Collect();
                Thread.Sleep(600);
                if (bgw.CancellationPending)
                {
                    return;
                }
                if (videoSource == null)
                {
                    continue;
                }
                Bitmap bitmap = videoSourcePlayer1.GetCurrentVideoFrame();
                if (bitmap == null)
                {
                    continue;
                }
                using (Image img = new Bitmap(bitmap))
                {
                    bitmap.Dispose();
                    using (Mat scr = new Image<Gray, byte>(img as Bitmap).Mat)
                    {
                        Rectangle[] recs = frontFace_detect.DetectMultiScale(scr);
                        if (recs.Length != 1)
                        {
                            bgw.ReportProgress(0, "wrong picture, face count:" + recs.Length + "\r\r\n");
                            continue;
                        }
                        lock (mylock)
                        {
                            if (this.needCamPic)//截取摄像头图片
                            {
                                bgw.ReportProgress(1, ImageUtil.CopyBitmap(img as Bitmap));
                                this.needCamPic = false;
                            }
                        }
                        using (Mat scr2 = new Mat(scr, recs[0]))
                        {
                            FaceRecognizer.PredictionResult result = face_recognize.Predict(scr2);
                            //name中存的是上面文本框存的各个人脸对应的名字，与label也是一一对应的
                            bgw.ReportProgress(0, name[result.Label] + "   distance:" + result.Distance.ToString("0.00") + "\r\r\n");
                        }
                    }
                }
            }
        }

        private void Bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            if (e.ProgressPercentage == 0)
            {
                AppendText(e.UserState as string);
            }
            if (e.ProgressPercentage == 1)
            {
                Bitmap b = e.UserState as Bitmap;
                string tempFile = "temp.jpg";
                if (File.Exists(tempFile))
                {
                    File.Delete(tempFile);
                }
                b.Save(tempFile);
                using (Mat scr = new Image<Gray, byte>(tempFile).Mat)
                {
                    Rectangle[] recs = frontFace_detect.DetectMultiScale(scr);
                    //Rectangle[] recs = profileFace_detect.DetectMultiScale(scr);
                    if (recs.Length != 1)
                    {
                        AppendText("wrong picture, face count:" + recs.Length + " \r\r\n");
                        return;
                    }
                    if (scrRegister != null)
                    {
                        scrRegister.Dispose();
                    }
                    scrRegister = new Mat(scr, recs[0]);
                    pictureBoxSelected.Image = scrRegister.Bitmap;
                }
            }
        }

        private void FrmEmgu_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                if (bgw.IsBusy)
                {
                    bgw.CancelAsync();
                }
                pnlVideo.Controls.Clear();
                videoSourcePlayer1.Stop();
                videoSourcePlayer1.Dispose();
            }
            catch (Exception)
            {
            }
        }
        private bool needCamPic = false;
        private object mylock = new object();
        private void btnFromVideo_Click(object sender, EventArgs e)
        {
            lock (mylock)
            {
                needCamPic = true;
            }
        }
    }
}
